package com.runjian.rundo.iot.agent.iec104.parser;

import java.util.HashMap;
import java.util.Map;

import org.springframework.stereotype.Component;

import com.runjian.rundo.iot.agent.iec104.consts.Cot;
import com.runjian.rundo.iot.agent.iec104.consts.Ti;
import com.runjian.rundo.iot.agent.iec104.parser.frame.ASDUFrame;
import com.runjian.rundo.iot.agent.iec104.parser.frame.IIec104Frame;
import com.runjian.rundo.iot.agent.iec104.parser.frame.control.process.*;
import com.runjian.rundo.iot.agent.iec104.parser.frame.control.system.*;
import com.runjian.rundo.iot.agent.iec104.parser.frame.monitoring.process.*;
import com.runjian.rundo.iot.agent.iec104.parser.frame.monitoring.system.M_EI_NA_1;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ByteUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * asdu的解析器
 *
 * @author CaoQingHua
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ASDUParser implements Parser {

    private static final Map<Ti, Class<? extends ASDUFrame>> CLASS_MAP = new HashMap<>();

    static {
        // **************************** 监视方向上的过程信息 *************************************
        CLASS_MAP.put(Ti.M_SP_NA_1, M_SP_NA_1.class);
        CLASS_MAP.put(Ti.M_DP_NA_1, M_DP_NA_1.class);
        CLASS_MAP.put(Ti.M_ME_NA_1, M_ME_NA_1.class);
        CLASS_MAP.put(Ti.M_ME_NB_1, M_ME_NB_1.class);
        CLASS_MAP.put(Ti.M_ME_NC_1, M_ME_NC_1.class);
        CLASS_MAP.put(Ti.M_SP_TB_1, M_SP_TB_1.class);
        CLASS_MAP.put(Ti.M_DP_TB_1, M_DP_TB_1.class);
        CLASS_MAP.put(Ti.M_ME_TF_1, M_ME_TF_1.class);
        CLASS_MAP.put(Ti.M_IT_NA_1, M_IT_NA_1.class);
        // **************************** 监视方向上的系统信息 *************************************
        CLASS_MAP.put(Ti.M_EI_NA_1, M_EI_NA_1.class);
        // **************************** 控制方向上的系统信息 *************************************
        CLASS_MAP.put(Ti.C_CI_NA_1, C_CI_NA_1.class);
        CLASS_MAP.put(Ti.C_IC_NA_1, C_IC_NA_1.class);
        CLASS_MAP.put(Ti.C_CS_NA_1, C_CS_NA_1.class);
        CLASS_MAP.put(Ti.C_RP_NA_1, C_RP_NA_1.class);
        CLASS_MAP.put(Ti.C_RD_NA_1, C_RD_NA_1.class);
        CLASS_MAP.put(Ti.C_RD_NA_2, C_RD_NA_2.class);
        // **************************** 控制方向上的过程信息 *************************************
        CLASS_MAP.put(Ti.C_SC_NA_1, C_SC_NA_1.class);
        CLASS_MAP.put(Ti.C_DC_NA_1, C_DC_NA_1.class);
        CLASS_MAP.put(Ti.C_RC_NA_1, C_RC_NA_1.class);
        CLASS_MAP.put(Ti.C_SE_NA_1, C_SE_NA_1.class);
        CLASS_MAP.put(Ti.C_SE_NA_2, C_SE_NA_2.class);
        CLASS_MAP.put(Ti.C_SE_NB_1, C_SE_NB_1.class);
        CLASS_MAP.put(Ti.C_SE_NC_1, C_SE_NC_1.class);
    }

    public IIec104Frame iIec104Frame(Ti ti, short address) {
        Class<? extends ASDUFrame> aClass = CLASS_MAP.get(ti);
        if (null == aClass) {
            Assert.isTrue(Boolean.FALSE, "生成ASDU I 侦信息对象失败,当前类型还未实现,Ti:{},设备地址:{}", ti, address);
        }
        try {
            ASDUFrame res = aClass.getDeclaredConstructor().newInstance();
            res.setTi(ti);
            res.setAddress(address);
            // 初始化
            res.init();
            return IIec104Frame.builder().asdu(res).build();
        } catch (InstantiationException | IllegalAccessException ae) {
            Assert.isTrue(Boolean.FALSE, "生成ASDU I 侦信息对象失败,Ti:{},设备地址:{}", ti, address);
        } catch (Exception e) {
            Assert.isTrue(Boolean.FALSE, "生成ASDU I 侦信息对象异常,Ti:{},设备地址:{},异常信息:", ti, address, e);
        }
        return null;
    }

    @Override
    public ASDUFrame analysis(byte[] msg) throws Exception {
        // 解析ASDU
        Ti ti = Ti.valueOfCode((short)(msg[6] & 0xff));
        Class<? extends ASDUFrame> aClass = CLASS_MAP.get(ti);
        if (null == aClass) {
            throw new ParserException(StrUtil.format("无法解析的I侦报文ASDU,当前解析类型为:{}", ti));
        }
        ASDUFrame res = null;
        res = aClass.getDeclaredConstructor().newInstance();
        res.setTi(ti);
        res.setSq((msg[7] & 0x80) == 128);
        res.setNumber((short)(msg[7] & 0x7f));
        res.setT((msg[8] & 0x80) == 128);
        res.setPN((msg[8] & 0x40) == 64);
        res.setCot(Cot.valueOfCode((short)(msg[8] & 0x3f)));
        res.setAddress(ByteUtil.bytesToShort(new byte[] {msg[10], msg[11]}));
        // 解析详细内容体
        res.analysisInfo(msg);
        return res;
    }

}
